package lk.icta.core.service.impl;

import lk.icta.common.AppConstants;
import lk.icta.common.ELandRuntimeException;
import lk.icta.common.ErrorCodes;
import lk.icta.common.entity.GNDivision;
import lk.icta.common.service.DocumentManagementService;
import lk.icta.common.service.MailSenderService;
import lk.icta.common.service.SmsSenderService;
import lk.icta.core.dao.*;
import lk.icta.core.dto.LandAssociationDetails;
import lk.icta.core.dto.LandDetails;
import lk.icta.core.entity.*;
import lk.icta.core.service.AlienationManagementService;
import lk.icta.core.service.LandManagementService;
import lk.icta.core.service.ScheduleManagementService;
import lk.icta.user.common.Permission;
import lk.icta.user.entity.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

public class LandManagementServiceImpl implements LandManagementService {

    private static final Logger logger = LoggerFactory.getLogger(LandManagementServiceImpl.class);

    private final LandDAO landDAO;
    private final AlienationDAO alienationDAO;
    private final AlienationManagementService alienationManagementService;
    private final DocumentManagementService documentManagementService;
    private final LandAssociationDao landAssociationDao;
    private final ScheduleManagementService scheduleManagementService;
    private final MailSenderService mailSenderService;
    private final SmsSenderService smsSenderService;

    public LandManagementServiceImpl(LandDAO landDAO, AlienationDAO alienationDAO,
                                     AlienationManagementService alienationManagementService,
                                     DocumentManagementService documentManagementService,
                                     LandAssociationDao landAssociationDao,
                                     ScheduleManagementService scheduleManagementService, MailSenderService mailSenderService,
                                     SmsSenderService smsSenderService) {
        this.landDAO = landDAO;
        this.alienationDAO = alienationDAO;
        this.alienationManagementService = alienationManagementService;
        this.documentManagementService = documentManagementService;
        this.landAssociationDao = landAssociationDao;
        this.scheduleManagementService = scheduleManagementService;
        this.mailSenderService = mailSenderService;
        this.smsSenderService = smsSenderService;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void addInitialUnAlienatedLand(Land land, User user, String language) {
        logger.debug("Adding an initial UnAlienated land {} by {}", land.getNameOfTheLand(), user.getUsername());
        if (user.isAuthorized(Permission.INITIAL_LAND_DATA_ENTRY)) {
            land.setApprovalState(Land.ApprovalState.DATA_ENTRY);
            land.setAlienationState(Land.AlienationState.UNALIENATED);
            //land.setMode(Land.Mode.DETAIL);
            //land.setPrefLanguage(language);
            landDAO.addLand(land, user);
            if (land.getDocumentList() != null) {
                try {
                    documentManagementService.uploadfiles(user, land.getDocumentList(), land.getId());
                } catch (Exception e) {
                    e.printStackTrace();
                    logger.error(e.getMessage());
                }
            }
        } else {
            handleELandRuntimeException(user.getUsername() + " doesn't have permission to add land",
                    ErrorCodes.UNAUTHORIZED_ACCESS);
        }
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void addInitialAlienatedLand(Land land, Alienation alienation, User user, String language) {
        logger.debug("Adding an initial alienated land {} by {}", land.getNameOfTheLand(), user.getUsername());

        if (!(user.isAuthorized(Permission.INITIAL_LAND_DATA_ENTRY) &&
                user.isAuthorized(Permission.INITIAL_ALIENATION_DATA_ENTRY))) {

            handleELandRuntimeException(user.getUsername() + " doesn't have permission to add land",
                    ErrorCodes.UNAUTHORIZED_ACCESS);
            return;
        }

        if (alienation.getAlienationMethod().getId() != AppConstants.LONG_TERM_LEASE &&
                alienation.getAlienationMethod().getId() != AppConstants.LAND_GRANT &&
                alienationManagementService.isExistsReference(alienation.getReferenceNo(), user)) {
            handleELandRuntimeException("Duplicate entry", ErrorCodes.DUPLICATE_ENTRY);
        }

       // land.setMode(Land.Mode.INITIAL);
        land.setApprovalState(Land.ApprovalState.DATA_ENTRY);
        land.setAlienationState(Land.AlienationState.ALIENATED);
       // land.setMode(Land.Mode.DETAIL);
        //land.setPrefLanguage(language);
        landDAO.addLand(land, user);
        if (land.getDocumentList() != null) {
            try {
                documentManagementService.uploadfiles(user, land.getDocumentList(), land.getId());
            } catch (Exception e) {
                e.printStackTrace();
                logger.error(e.getMessage());
            }
        }
        alienation.setLand(land);
        alienationManagementService.addInitialAlienation(alienation, user);

    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void addDetailUnAlienatedLand(Land land, long alienationId, LandAssociation landAssociation,
                                         User user, String language) {
        logger.debug("Adding a detail Un-Alienated land {} by {}", land.getId(), user.getUsername());
        if (!user.isAuthorized(Permission.DETAIL_LAND_DATA_ENTRY)) {
            handleELandRuntimeException(user.getUsername() + " doesn't have permission to add detailed land information",
                    ErrorCodes.UNAUTHORIZED_ACCESS);
        }

        Land initLand = landDAO.getLandByIdUKey(land.getId(), user);

        if (initLand != null && initLand.getVersion() != land.getVersion()) {
            handleELandRuntimeException("The record already modified", ErrorCodes.ILLEGAL_STATE);
            return;
        }

        if (initLand != null) {

            logger.debug("Land Id before archive initial record {} - ", initLand.getId());
            initLand.setAlienationState(Land.AlienationState.ARCHIVED);
            landDAO.updateLand(initLand, user);

            logger.debug("Land Id after archive initial record {} - {}", initLand.getId(),
                    initLand.getAlienationState());
                /* Adding detail land record. */
            logger.debug("initial land record id{} - ", initLand.getId());
            //land.setInitialRecordId(initLand.getId());
            //land.setMode(Land.Mode.DETAIL);
            //land.setPrefLanguage(language);
            land.setActive(true);
            land.setId(0);

            //set the kml data
            String kmlData = initLand.getKmlString();
            if (kmlData != null) {
                land.setKmlString(kmlData);
            }

            //set the kml middle point
            String kmlMiddlePoint = initLand.getKmlMiddlePoint();
            if (kmlMiddlePoint != null) {
                land.setKmlMiddlePoint(kmlMiddlePoint);
            }


        } else {
            logger.debug("Sub divide land recordl record {} - ");
                /* Adding detail land record. */
            land.setAlienationState(Land.AlienationState.UNALIENATED);
            //land.setInitialRecordId(0);
           // land.setMode(Land.Mode.DETAIL);
           // land.setPrefLanguage(language);
            land.setActive(true);
            land.setId(0);
        }

        logger.debug("Land Id before add detail land {} - ", land.getId());
        land.setApprovalState(Land.ApprovalState.PENDING);
        landDAO.addLand(land, user);
        logger.debug("Land Id after add detail land {} - ", land.getId());

       /* if (land.getInitialRecordId() > 0) {
            logger.debug("upload existing files of initial land : Initial Record Id {}", land.getInitialRecordId());
            try {
                logger.debug("Land Id {} : ", land.getId());
                documentManagementService.uploadExistingfiles(user, land.getInitialRecordId(), land.getId(),
                        ModuleType.INITIAL_LAND, ModuleType.DETAILED_LAND);
            } catch (Exception e) {
                e.printStackTrace();
                logger.error(e.getMessage());
            }
        }*/

        if (land.getDocumentList() != null) {
            try {
                logger.debug("Uploading files - Land Id {} : ", land.getId());
                documentManagementService.uploadfiles(user, land.getDocumentList(), land.getId());

            } catch (Exception e) {
                e.printStackTrace();
                logger.error(e.getMessage());
            }
        }

        //TODO: move to alienation service
        if (alienationId != 0) {
            Alienation initAlienation = alienationDAO.getAlienationById(alienationId, user);
                /* Archive initial record */
            if (initAlienation != null) {
                initAlienation.setStatus(Alienation.State.ARCHIVED);
                alienationDAO.updateAlienation(initAlienation, user);
            }
        }

        //save land Association
        addLandAssociation(landAssociation, land, user);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void addSubDividedLand(Land land, User user, String language) {
        logger.debug("Adding a sub divided land {} by {}", land.getId(), user.getUsername());
        if (user.isAuthorized(Permission.DETAIL_LAND_DATA_ENTRY)) {

            logger.debug("Sub divide land record {} - ");

            /* Adding detail land record. */
            land.setAlienationState(Land.AlienationState.UNALIENATED);
            //land.setInitialRecordId(0);
           // land.setMode(Land.Mode.SUBDIVIDED);
           // land.setPrefLanguage(language);
            land.setActive(true);
            land.setId(0);
            land.setApprovalState(Land.ApprovalState.PENDING);
            landDAO.addLand(land, user);

            if (land.getDocumentList() != null) {
                try {
                    logger.debug("Uploading files - Land Id {} : ", land.getId());
                    documentManagementService.uploadfiles(user, land.getDocumentList(), land.getId());

                } catch (Exception e) {
                    e.printStackTrace();
                    logger.error(e.getMessage());
                }
            }

        } else {
            handleELandRuntimeException(user.getUsername() + " doesn't have permission to add sub divided land information",
                    ErrorCodes.UNAUTHORIZED_ACCESS);
        }
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void editSubDividedLand(Land land, User user, String language) {
        logger.debug("Updating a sub divided land {} by {}", land.getId(), user.getUsername());
        if (user.isAuthorized(Permission.DETAIL_LAND_DATA_ENTRY)) {

            logger.debug("Sub divide land record {} - ");

            land.setAlienationState(Land.AlienationState.UNALIENATED);
            //land.setInitialRecordId(0);
            //land.setMode(Land.Mode.SUBDIVIDED);
            //land.setPrefLanguage(language);
            land.setActive(true);
            land.setId(land.getId());

            Land existingLand = landDAO.getLandById(land.getId());

            //set the kml data
            String kmlData = existingLand.getKmlString();
            if (kmlData != null) {
                land.setKmlString(kmlData);
            }

            //set the kml middle point
            String kmlMiddlePoint = existingLand.getKmlMiddlePoint();
            if (kmlMiddlePoint != null) {
                land.setKmlMiddlePoint(kmlMiddlePoint);
            }



            land.setApprovalState(Land.ApprovalState.PENDING);
            land.getLifeCycleInfo().setCreatedUser(existingLand.getLifeCycleInfo().getCreatedUser());
            land.getLifeCycleInfo().setCreatedTimeStamp(existingLand.getLifeCycleInfo().getCreatedTimeStamp());

            landDAO.updateLand(land, user);
            logger.debug("Land Id after edit detail land {} - ", land.getId());


            if (land.getDocumentList() != null) {
                try {
                    logger.debug("Uploading files - Land Id {} : ", land.getId());
                    documentManagementService.uploadfiles(user, land.getDocumentList(), land.getId());

                } catch (Exception e) {
                    e.printStackTrace();
                    logger.error(e.getMessage());
                }
            }
        } else {
            handleELandRuntimeException(user.getUsername() + " doesn't have permission to add sub divided land information",
                    ErrorCodes.UNAUTHORIZED_ACCESS);
        }
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void updateDetailUnAlienatedLand(Land land, long alienationId, LandAssociation landAssociation, User user,
                                            String language) {
        logger.debug("Updating a detail Un-Alienated land {} by {}", land.getNameOfTheLand(), user.getUsername());
        if (user.isAuthorized(Permission.DETAIL_LAND_DATA_MODIFICATION)) {
            Land existingLand = getLandByIdUKey(land.getId(), user);
            if (existingLand.getAlienationState() == Land.AlienationState.ARCHIVED) {
                handleELandRuntimeException("Attempt to update archived land information. [Land Name: " +
                        land.getNameOfTheLand() + "]", ErrorCodes.ARCHIVED_LAND_UPDATING);
            }

           // land.setMode(Land.Mode.DETAIL);
            //land.setPrefLanguage(language);
            land.setActive(true);

            //set the kml data
            String kmlData = existingLand.getKmlString();
            if (kmlData != null) {
                land.setKmlString(kmlData);
            }

            //set the kml middle point
            String kmlMiddlePoint = existingLand.getKmlMiddlePoint();
            if (kmlMiddlePoint != null) {
                land.setKmlMiddlePoint(kmlMiddlePoint);
            }



            land.setAlienationState(Land.AlienationState.UNALIENATED);
            land.setApprovalState(Land.ApprovalState.PENDING);
            land.getLifeCycleInfo().setCreatedUser(existingLand.getLifeCycleInfo().getCreatedUser());
            land.getLifeCycleInfo().setCreatedTimeStamp(existingLand.getLifeCycleInfo().getCreatedTimeStamp());
            landDAO.updateLand(land, user);
            if (land.getDocumentList() != null) {
                try {
                    documentManagementService.uploadfiles(user, land.getDocumentList(), land.getId());

                } catch (Exception e) {
                    e.printStackTrace();
                    logger.error(e.getMessage());
                }
            }
            if (alienationId != 0) {
                alienationDAO.removeAlienation(alienationId, user);
            }

            //save Land Association
            addLandAssociation(landAssociation, land, user);

        } else {
            handleELandRuntimeException(user.getUsername() +
                    " doesn't have permission to update detailed land information", ErrorCodes.UNAUTHORIZED_ACCESS);
        }
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void deleteDetailUnAlienatedLand(Land land, User user) {
        logger.debug("Deleting detail land {} by {}", land.getNameOfTheLand(), user.getUsername());
        if (user.isAuthorized(Permission.DETAIL_LAND_DATA_MODIFICATION)) {
            Land existingLand = landDAO.getLandByIdUKey(land.getId(), user);
            land.getLifeCycleInfo().setCreatedUser(existingLand.getLifeCycleInfo().getCreatedUser());
            land.getLifeCycleInfo().setCreatedTimeStamp(existingLand.getLifeCycleInfo().getCreatedTimeStamp());
            landDAO.deleteLand(existingLand, user);
        } else {
            handleELandRuntimeException(user.getUsername() +
                    " doesn't have permission to delete detailed land information", ErrorCodes.UNAUTHORIZED_ACCESS);
        }
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void deleteDetailUnAlienatedLandFromDatabase(Land land, User user) {
        //long initialRecordId = land.getInitialRecordId();
        if (user.isAuthorized(Permission.DETAIL_LAND_DATA_MODIFICATION)) {

            landDAO.deleteLandFromDatabase(land.getId(), user);

        } else {
            handleELandRuntimeException(user.getUsername() +
                    " doesn't have permission to delete detailed land information", ErrorCodes.UNAUTHORIZED_ACCESS);
        }
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void addDetailAlienatedLand(Land land, Alienation alienation, LandAssociation landAssociation, User user, String language) {
        logger.debug("Adding detail Alienated land {} by {}", land.getNameOfTheLand(), user.getUsername());
        if (user.isAuthorized(Permission.DETAIL_LAND_DATA_ENTRY) &&
                user.isAuthorized(Permission.DETAIL_ALIENATION_DATA_ENTRY)) {
            Land initLand = getLandByIdUKey(land.getId(), user);

            if (initLand != null && initLand.getVersion() != land.getVersion()) {
                handleELandRuntimeException("The record already modified", ErrorCodes.ILLEGAL_STATE);
                return;
            }

            initLand.setAlienationState(Land.AlienationState.ARCHIVED);
            landDAO.updateLand(initLand, user);

            /* Add new record */
            //land.setInitialRecordId(initLand.getId());
           // land.setMode(Land.Mode.DETAIL);
           // land.setPrefLanguage(language);
            land.setActive(true);
            land.setId(0);

            //set the kml data
            String kmlData = initLand.getKmlString();
            if (kmlData != null) {
                land.setKmlString(kmlData);
            }

            //set the kml middle point
            String kmlMiddlePoint = initLand.getKmlMiddlePoint();
            if (kmlMiddlePoint != null) {
                land.setKmlMiddlePoint(kmlMiddlePoint);
            }



            land.setApprovalState(Land.ApprovalState.PENDING);
            landDAO.addLand(land, user);

            if (land.getDocumentList() != null) {
                try {
                    logger.debug("Uploading files - Land Id {} : ", land.getId());
                    documentManagementService.uploadfiles(user, land.getDocumentList(), land.getId());

                } catch (Exception e) {
                    e.printStackTrace();
                    logger.error(e.getMessage());
                }
            }
            alienation.setLand(land);
            alienationManagementService.addDetailAlienation(alienation, user);

            //save Land Association
            addLandAssociation(landAssociation, land, user);

        } else {
            handleELandRuntimeException(user.getUsername() +
                    " doesn't have permission to add detailed land information", ErrorCodes.UNAUTHORIZED_ACCESS);
        }
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void updateDetailAlienatedLand(Land land, Alienation alienation, LandAssociation landAssociation, User user,
                                          String language) {

        logger.debug("----  Updating a detail alienated land {} by {}", land.getId(), user.getUsername());

        if (user.isAuthorized(Permission.DETAIL_LAND_DATA_MODIFICATION) ||
                user.isAuthorized(Permission.DETAIL_ALIENATION_DATA_MODIFICATION)) {
            Land existingLand = landDAO.getLandByIdUKey(land.getId(), user);
            if (existingLand.getAlienationState() == Land.AlienationState.ARCHIVED) {
                handleELandRuntimeException("Attempt to update archived land information. [Land Name: " +
                        land.getNameOfTheLand() + "]", ErrorCodes.ARCHIVED_LAND_UPDATING);
            }

            land.setActive(true);

            //set the kml data
            String kmlData = existingLand.getKmlString();
            if (kmlData != null) {
                land.setKmlString(kmlData);
            }

            //set the kml middle point
            String kmlMiddlePoint = existingLand.getKmlMiddlePoint();
            if (kmlMiddlePoint != null) {
                land.setKmlMiddlePoint(kmlMiddlePoint);
            }



            land.setAlienationState(Land.AlienationState.ALIENATED);
            land.setApprovalState(Land.ApprovalState.PENDING);
            land.getLifeCycleInfo().setCreatedUser(existingLand.getLifeCycleInfo().getCreatedUser());
            land.getLifeCycleInfo().setCreatedTimeStamp(existingLand.getLifeCycleInfo().getCreatedTimeStamp());
            landDAO.updateLand(land, user);
            if (land.getDocumentList() != null) {
                try {
                    documentManagementService.uploadfiles(user, land.getDocumentList(), land.getId());

                } catch (Exception e) {
                    e.printStackTrace();
                    logger.error(e.getMessage());
                }
            }
            alienation.setLand(land);
            alienationManagementService.updateDetailAlienation(alienation, user);

            //save Land Association
            LandAssociation existingLa = landAssociationDao.getAlienationMethodMetadataByLand(land.getId(), user);
            //Already there is a land association. When detailed form makes land alienation association should be removed
            if(existingLa != null){
            	landAssociationDao.removeLandAssociation(existingLa.getId(), user);
            } else {
            addLandAssociation(landAssociation, land, user);
            }

        } else {
            handleELandRuntimeException(user.getUsername() +
                    " doesn't have permission to update detailed land information", ErrorCodes.UNAUTHORIZED_ACCESS);
        }
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void deleteDetailAlienatedLand(Land land, Alienation alienation, User user) {
        logger.debug("Deleting a detail alienated land {} by {}", land.getNameOfTheLand(), user.getUsername());
        if (user.isAuthorized(Permission.DETAIL_LAND_DATA_MODIFICATION) ||
                user.isAuthorized(Permission.DETAIL_ALIENATION_DATA_MODIFICATION)) {
            Land existingLand = landDAO.getLandByIdUKey(land.getId(), user);
            land.getLifeCycleInfo().setCreatedUser(existingLand.getLifeCycleInfo().getCreatedUser());
            land.getLifeCycleInfo().setCreatedTimeStamp(existingLand.getLifeCycleInfo().getCreatedTimeStamp());
            landDAO.deleteLand(existingLand, user);
            alienationManagementService.deleteDetailAlienation(alienation, user);
        } else {
            handleELandRuntimeException(user.getUsername() +
                    " doesn't have permission to delete detailed land information", ErrorCodes.UNAUTHORIZED_ACCESS);
        }
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void deleteDetailAlienatedLandFromDatabase(Land land, long alienationId, User user) {
        logger.debug("Deleting a detail alienated land {} by {}", land.getNameOfTheLand(), user.getUsername());

        if (user.isAuthorized(Permission.DETAIL_LAND_DATA_MODIFICATION) ||
                user.isAuthorized(Permission.DETAIL_ALIENATION_DATA_MODIFICATION)) {

            landDAO.deleteLandFromDatabase(land.getId(), user);

            alienationManagementService.deleteAlienationFromDatabase(alienationId, user);
        } else {
            handleELandRuntimeException(user.getUsername() +
                    " doesn't have permission to delete detailed land information", ErrorCodes.UNAUTHORIZED_ACCESS);
        }
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void updateGEOInfo(Land land, User user) {
        logger.debug("Updating a detail land {} ", user.getUsername());
        if (user.isAuthorized(Permission.DETAIL_LAND_DATA_MODIFICATION)) {
            Land existingLand = landDAO.getLandByIdUKey(land.getId(), user);
            if (!existingLand.isActive()) {
                handleELandRuntimeException("Attempt to update archived land information. [Land id: " +
                        land.getId() + "]", ErrorCodes.ARCHIVED_LAND_UPDATING);
            }

            //update Geo info
            existingLand.setKmlString(land.getKmlString());
            existingLand.setKmlLandArea(land.getKmlLandArea());
            existingLand.setKmlMiddlePoint(land.getKmlMiddlePoint());

            //set the GeoParcelCode

            String middlePoint = land.getKmlMiddlePoint();

            StringTokenizer tokenizer = new StringTokenizer(middlePoint, ",");
            if (tokenizer.hasMoreTokens()) {
                String lon = tokenizer.nextToken();
                String lat = tokenizer.nextToken();
                String alt = tokenizer.nextToken();

                GeoParcelCode geoParcelCode;



            }

            existingLand.getLifeCycleInfo().setCreatedUser(existingLand.getLifeCycleInfo().getCreatedUser());
            existingLand.getLifeCycleInfo().setCreatedTimeStamp(existingLand.getLifeCycleInfo().getCreatedTimeStamp());
            landDAO.updateLand(existingLand, user);
        } else {
            handleELandRuntimeException(user.getUsername() +
                    " doesn't have permission to update detailed land information", ErrorCodes.UNAUTHORIZED_ACCESS);
        }
    }

    //todo: to be removed, used in land grants
    @Transactional(propagation = Propagation.REQUIRED)
    public void updateLand(Land land, User user) {
        logger.debug("Updating land [Land Name: {}]", land.getNameOfTheLand());
        if (user.isAuthorized(Permission.INITIAL_LAND_DATA_MODIFICATION) ||
                user.isAuthorized(Permission.DETAIL_LAND_DATA_MODIFICATION)) {
            Land existing = landDAO.getLandByIdUKey(land.getId(), user);
            if (existing.getAlienationState() == Land.AlienationState.ARCHIVED) {
                handleELandRuntimeException("Attempt to update archived land information. [Land Name: " +
                        land.getNameOfTheLand() + "]", ErrorCodes.ARCHIVED_LAND_UPDATING);
            }
            land.getLifeCycleInfo().setCreatedUser(existing.getLifeCycleInfo().getCreatedUser());
            land.getLifeCycleInfo().setCreatedTimeStamp(existing.getLifeCycleInfo().getCreatedTimeStamp());
            landDAO.updateLand(land, user);
            if (land.getDocumentList() != null) {
                logger.debug("Update Land - Uploading files - Land Id {} : ", land.getId());
                documentManagementService.uploadfiles(user, land.getDocumentList(), land.getId());
            }
        } else {
            handleELandRuntimeException(user.getUsername() + " doesn't have permission to update land",
                    ErrorCodes.UNAUTHORIZED_ACCESS);
        }
    }

    //todo: to be removed, used in land grants
    @Transactional(propagation = Propagation.REQUIRED)
    public void completeInitLandInformation(Land land, User user) {
        logger.debug("Completing land information of land {} by {}", land.getId(), user.getUsername());
        if (user.isAuthorized(Permission.INITIAL_LAND_DATA_MODIFICATION)) {

            landDAO.updateLand(land, user);
        } else {
            handleELandRuntimeException(user.getUsername() + " doesn't have permission to complete initial data entry",
                    ErrorCodes.UNAUTHORIZED_ACCESS);
        }
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void approveLandInformation(Land land, String approvalComment, User user) {
        logger.debug("Approving land {} by {}", land.getId(), user.getUsername());
        if (!user.isAuthorized(Permission.LAND_APPROVAL)) {
            handleELandRuntimeException(user.getUsername() + " doesn't have permission to approve land information",
                    ErrorCodes.UNAUTHORIZED_ACCESS);
        } else {
            Land existing = landDAO.getLandByIdUKey(land.getId(), user);

            if (existing.getVersion() != land.getVersion()) {
                handleELandRuntimeException("The record already modified", ErrorCodes.ILLEGAL_STATE);
                return;
            }

            if (Land.AlienationState.ARCHIVED.equals(existing.getAlienationState())) {
                handleELandRuntimeException("Attempt to approve archived land information. [Land Name: " +
                        land.getNameOfTheLand() + "]", ErrorCodes.ARCHIVED_LAND_APPROVAL);
            } else if (Land.ApprovalState.APPROVED.equals(existing.getApprovalState())) {
                handleELandRuntimeException("Attempt to approve already approved land " + existing.getId(),
                        ErrorCodes.APPROVED_LAND_APPROVAL);
            } else if (Land.ApprovalState.REJECTED.equals(existing.getApprovalState())) {
                handleELandRuntimeException("Attempt to approve already rejected land " + existing.getId(),
                        ErrorCodes.REJECTED_LAND_APPROVAL);
            } else if (Land.ApprovalState.DATA_ENTRY.equals(existing.getApprovalState())) {
                handleELandRuntimeException("Attempt to approve incomplete record " + existing.getId(),
                        ErrorCodes.INCOMPLETE_LAND_APPROVAL);
            } else if (Land.ApprovalState.PENDING.equals(existing.getApprovalState())) {
                logger.debug("Approve land {} by {}", land.getNameOfTheLand(), user.getUsername());
                existing.setApprovalState(Land.ApprovalState.APPROVED);
                //existing.setApprovalNote(land.getApprovalNote());

                //set Approval Comment
                setApprovalComment(existing, approvalComment, true, user);


                if (Land.AlienationState.UNALIENATED.equals(existing.getAlienationState())) {
                    landDAO.updateLand(existing, user);
                } else {
                    landDAO.updateLand(existing, user);
                    Alienation approveAlienation = alienationDAO.getActiveAlienationByLandId(existing.getId(), user);
                    alienationManagementService.approveAlienation(approveAlienation, user);
                }

                LandAssociation landAssociation = landAssociationDao.getAlienationMethodMetadataByLand(existing.getId(), user);
                if (landAssociation != null) {
                    landAssociation.setAssociationState(LandAssociation.AssociationState.DETAIL_LAND_DATA_ENTRY_APPROVED);
                    landAssociationDao.updateLandAlienationMetadata(landAssociation, user);
                }

                //mailSenderService.setLandManagementService(landManagementService);

                String emailSubject = "Land Details Approval";
                String landDetails = "Land details of the land referenced by serial number : " + land.getId();
                Land l = landDAO.getLandById(land.getId());

                String gisParcelCode = null;

                String emailedMessage = null;

                if (gisParcelCode != null) {
                    emailedMessage = landDetails + " with GIS Parcel Code : " + gisParcelCode + " has been approved by ";
                } else {
                    emailedMessage = landDetails + " has been approved by ";
                }
                GNDivision GNDiv = this.getLandById(land.getId()).getGnDivision();
                mailSenderService.notifyToAll(GNDiv, user, emailSubject, emailedMessage);

                //send sms
                if (l.getLifeCycleInfo().getCreatedUser().getMobile() != null) {

                    smsSenderService.sendSMS(emailedMessage+" " +user.getFullName(), l.getLifeCycleInfo().getCreatedUser().getMobile());
                }

            } else {
                handleELandRuntimeException("Unhandled error occurred", ErrorCodes.ILLEGAL_STATE);
            }
        }
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void rejectLandInformation(Land land, String approvalComment, User user) {

        logger.debug("Rejecting land {} by {}", land.getId(), user.getUsername());
        if (!user.isAuthorized(Permission.LAND_APPROVAL)) {
            handleELandRuntimeException(user.getUsername() + " doesn't have permission to reject land information",
                    ErrorCodes.UNAUTHORIZED_ACCESS);
            return;
        }

        Land existing = landDAO.getLandByIdUKey(land.getId(), user);
        if (existing.getVersion() != land.getVersion()) {
            handleELandRuntimeException("The record already modified", ErrorCodes.ILLEGAL_STATE);
            return;
        }

        if (Land.AlienationState.ARCHIVED.equals(existing.getAlienationState())) {
            handleELandRuntimeException("Attempt to reject archived land information. [Land Name: " +
                    land.getNameOfTheLand() + "]", ErrorCodes.ARCHIVED_LAND_REJECTION);
        } else if (Land.ApprovalState.APPROVED.equals(existing.getApprovalState())) {
            handleELandRuntimeException("Attempt to reject already approved land " + existing.getId(),
                    ErrorCodes.APPROVED_LAND_REJECTION);
        } else if (Land.ApprovalState.REJECTED.equals(existing.getApprovalState())) {
            handleELandRuntimeException("Attempt to reject already rejected land " + existing.getId(),
                    ErrorCodes.REJECTED_LAND_REJECTION);
        } else if (Land.ApprovalState.DATA_ENTRY.equals(existing.getApprovalState())) {
            handleELandRuntimeException("Attempt to reject incomplete record " + existing.getId(),
                    ErrorCodes.INCOMPLETE_LAND_REJECTION);
        } else if (Land.ApprovalState.PENDING.equals(existing.getApprovalState())) {
            logger.debug("Reject land information. [Land Name: {}]", land.getNameOfTheLand());
            existing.setApprovalState(Land.ApprovalState.REJECTED);

            //set Approval Comment
            setApprovalComment(existing, approvalComment, false, user);

            if (Land.AlienationState.UNALIENATED.equals(existing.getAlienationState())) {
                landDAO.updateLand(existing, user);
            } else {
                landDAO.updateLand(existing, user);
                Alienation rejectAlienation = alienationDAO.getActiveAlienationByLandId(existing.getId(), user);
                alienationManagementService.rejectAlienation(rejectAlienation, user);
            }

            /** Send emails **/

            String emailSubject = "Land Details Rejection";
            String landDetails = "Land details of the land referenced by serial number : " + land.getId();
            Land l = landDAO.getLandById(land.getId());

            String gisParcelCode = null;


            String emailedMessage = null;

                if (gisParcelCode != null) {
                    emailedMessage = landDetails + " with GIS Parcel Code : " + gisParcelCode + " has been rejected by ";
                } else {
                    emailedMessage = landDetails + " has been rejected by ";
                }
                GNDivision GNDiv = this.getLandById(land.getId()).getGnDivision();
                mailSenderService.notifyToAll(GNDiv, user, emailSubject, emailedMessage);

                //send sms
                if (l.getLifeCycleInfo().getCreatedUser().getMobile() != null) {
                    logger.debug("Pno {}",l.getLifeCycleInfo().getCreatedUser().getMobile());
                    smsSenderService.sendSMS(emailedMessage +" " +user.getFullName(), l.getLifeCycleInfo().getCreatedUser().getMobile());
                }

                /** end **/
            } else {
                handleELandRuntimeException("Unhandled error occurred", ErrorCodes.ILLEGAL_STATE);
            }
        
    }

    private void setApprovalComment(Land existing, String approvalComment, boolean approve, User user) {

        String approveStr = "";
        if (approve) {
            approveStr = "Approved";
        } else {
            approveStr = "Rejected";
        }
        //Stop creating empty new lines to log
        if(approvalComment != null && !("".equals(approvalComment.trim()))){
            approvalComment = approvalComment  + "\n"  ;
        }

        if (existing.getApprovalNote() != null) {
            String approvalNote = existing.getApprovalNote();
            if(!("".equals(approvalNote.trim()))){
                approvalNote = approvalNote  + "\n"  ;
            }
            //existing.setApprovalNote(existing.getApprovalNote() + "\n" + approvalComment + "\n" + approveStr + " by :" + user.getUsername() + ", Date : " + new Date() + "\n");
            existing.setApprovalNote( approvalNote + approvalComment + approveStr + " by :" + user.getUsername() + ", Date : " + new Date() + ".");
        } else {
            existing.setApprovalNote(approvalComment+ approveStr + " by :" + user.getUsername() + ", Date : " + new Date() + ".");
        }

    }

    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public Land getLandByIdUKey(long idUKey, User user) {
        logger.debug("getLandByIdUKey {}", idUKey);
        Land land = landDAO.getLandByIdUKey(idUKey, user);

        if (AppConstants.ADMIN.equals(land.getLifeCycleInfo().getCreatedUser().getUsername())) {
            land.setMigratedRecord(true);
        }


        return land;
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getInitialActiveLandsByDSDivision(int dsDivisionId, User user) {
        return populateBasicLandDetailsForInitReport(landDAO.getInitialActiveLandsByDSDivision(dsDivisionId, user), user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getAllLandsReport(int divisionId, String searchBy, Land.AlienationState alienationState,
                                               int alienationMethodId, int geoTypeId, String language, int pageNo) {
        return populateSpecificLandDetails(landDAO.searchAllLandsReport(divisionId, searchBy, alienationState,
                alienationMethodId, geoTypeId, pageNo), language);
    }

    private List<LandDetails> populateSpecificLandDetails(List<Land> landList, String language) {
        List<LandDetails> landDetailsList = new ArrayList<LandDetails>();
        for (Land land : landList) {
            LandDetails landDetail = new LandDetails();

            landDetail.setGNDivision(land.getGnDivision().getGNDivisionNameByLanguage(language));
            landDetail.setDSDivision(land.getGnDivision().getDsDivision().getDSDivisionNameByLanguage(language));
            landDetail.setId(land.getId());
            landDetail.setNameOfTheLand(land.getNameOfTheLand());
            landDetail.setPlanNumber(land.getPlanNumber());
            landDetail.setLandExtent(land.getLandExtent());

            landDetail.setLotNumber(land.getLotNumber());
            landDetailsList.add(landDetail);

        }
        return landDetailsList;
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getInitialActiveLandsByGNDivision(int gnDivisionId, User user) {
        logger.debug("Get Initial Lands By GN Division - {} By User - {}", gnDivisionId, user.getUsername());
        return populateBasicLandDetailsForInitReport(landDAO.getInitialActiveLandsByGNDivision(gnDivisionId, user), user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Land> getDetailActiveLandsByGNDivision(int gnDivisionId, User user) {
        logger.debug("Get Detailed Lands By GN Division - {} By User - {}", gnDivisionId, user.getUsername());
        return landDAO.getDetailActiveLandsByGNDivision(gnDivisionId, user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getDetailActiveLandDetailsByGNDivision(int gnDivisionId, User user) {
        logger.debug("Get Detailed Lands By GN Division - {} By User - {}", gnDivisionId, user.getUsername());
        return populateBasicLandDetails(landDAO.getDetailActiveLandsByGNDivision(gnDivisionId, user), user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getIncompleteDetailLandsByGNDivision(int gnDivisionId, User user) {
        logger.debug("Get Incomplete Detailed Lands By GN Division - {} By User - {}", gnDivisionId, user.getUsername());
        return populateBasicLandDetails(landDAO.getIncompleteDetailLandsByGNDivision(gnDivisionId, user), user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getAllActivePendingLands(User user) {
        logger.debug("get All Active Pending Lands by {}", user.getUsername());
        return populateBasicLandDetails(landDAO.getAllActivePendingLands(user), user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getActivePendingLandsByGNDivision(int gnDivisionId, User user) {
        return populateBasicLandDetails(landDAO.getActivePendingLandsByGNDivision(gnDivisionId, user), user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getRejectedLandsByGNDivision(int gnDivisionId, User user) {
        return populateBasicLandDetails(landDAO.getRejectedLandsByGNDivision(gnDivisionId, user), user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getSubDividedLandsTobeApproveByGNDivision(int gnDivisionId, User user) {
        return populateBasicLandDetails(landDAO.getSubDividedLandsTobeApproveByGNDivision(gnDivisionId, user), user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getSubDividedLandsTobeEditByGNDivision(int gnDivisionId, User user) {
        return populateBasicLandDetails(landDAO.getSubDividedLandsTobeEditByGNDivision(gnDivisionId, user), user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getAllIncompleteDetailLands(User user) {
        logger.debug("Get all Incomplete land records by {}", user.getUsername());
        return populateBasicLandDetails(landDAO.getAllIncompleteDetailLands(user), user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getActivePendingLandsByDSDivision(User user) {
        return populateBasicLandDetails(landDAO.getActivePendingLandsByDSDivision(user), user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getActivePendingLandsByDSDivisionId(int dsDivisionId, User user){
        return populateBasicLandDetails(landDAO.getActivePendingLandsByDSDivisionId(dsDivisionId, user),user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getRejectedLandsByDSDivision(User user) {
        return populateBasicLandDetails(landDAO.getRejectedLandsByDSDivision(user), user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getSubDividedLandsTobeApproveByDSDivision(User user) {
        return populateBasicLandDetails(landDAO.getSubDividedLandsTobeApproveByDSDivision(user), user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getSubDividedLandsTobeEditByDSDivision(User user) {
        return populateBasicLandDetails(landDAO.getSubDividedLandsTobeEditByDSDivision(user), user);
    }

    private List<LandDetails> populateBasicLandDetails(List<Land> landList, User user) {
        List<LandDetails> landDetailsList = new ArrayList<LandDetails>();
        for (Land land : landList) {
            LandDetails landDetail = new LandDetails();
            landDetail.setId(land.getId());
            landDetail.setNameOfTheLand(land.getNameOfTheLand());
            landDetail.setPlanNumber(land.getPlanNumber());
            landDetail.setLandExtent(land.getLandExtent());

            landDetail.setLotNumber(land.getLotNumber());

            landDetailsList.add(landDetail);
        }
        return landDetailsList;
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getDetailLandReportByGNDivision(int gnDivisionId, User user) {
        logger.debug("Get  Detailed Lands Report By GN Division - {} By User - {}", gnDivisionId, user.getUsername());
        return populateBasicLandDetails(landDAO.getDetailLandReportByGNDivision(gnDivisionId, user), user);
    }

    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public Land getLandById(long id) {
        logger.debug("getLandById {}", id);
        return landDAO.getLandById(id);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public Land getLandByRef(long referenceNumber) {
        return landDAO.getLandByRef(referenceNumber);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Land> getApprovedUnAlienatedLandsByGNDivision(int gnDivisionId, User user) {
        logger.debug("Loading approved, UnAlienated lands of GN Division {} by {}", gnDivisionId, user.getUsername());
        return landDAO.getApprovedUnAlienatedLandsByGNDivision(gnDivisionId, user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public Land getApprovedUnAlienatedLandById(long id) {
        logger.debug("Loading approved, UnAlienated lands by id{}", id);
        return landDAO.getApprovedUnAlienatedLandById(id);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Land> getAllApprovedUnAlienatedLands(User user) {
        logger.debug("Loading all approved, UnAlienated lands");
        return landDAO.getAllApprovedUnAlienatedLands(user);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void archiveLand(Land land, User user) {
        logger.debug("Archive land {} by {}", land.getId(), user.getUsername());
        land.setAlienationState(Land.AlienationState.ARCHIVED);
        landDAO.updateLand(land, user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getApprovedLandsByGNDivision(int gnDivisionId, User user) {
        logger.debug("Loading approved lands of GN Division {} by {}", gnDivisionId, user.getUsername());
        List<Land> landList = landDAO.getApprovedLandsByGNDivision(gnDivisionId, user);
        return populateBasicLandDetails(landList, user);
    }

    //todo: to be refactored. select distinct lands from AMMetadata table
    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getLandAssociationsToBeApproved(int gnDivisionId, User user) {
        logger.debug("Loading land associations to be approved  of GN Division {} by {}", gnDivisionId, user.getUsername());
        //List<Land> approvedLandList = landDAO.getApprovedLandsByGNDivision(gnDivisionId, user);
        List<Land> approvedLandList = new ArrayList<Land>();
        if (gnDivisionId == -1 && user.getDsDivisions() != null && user.getDsDivisions().size() > 0) {
            //approvedLandList = landDAO.searchAllActiveApprovedLandsOfDSDivision(user.getDsDivisions().iterator().next().getId());
            approvedLandList = landDAO.searchAllActiveApprovedUnalienatedLandsOfDSDivision(user.getDsDivisions().iterator().next().getId());
        } else {
            //approvedLandList = landDAO.getApprovedLandsByGNDivision(gnDivisionId, user);
            approvedLandList = landDAO.getApprovedUnAlienatedLandsByGNDivision(gnDivisionId, user);
        }

        // List<LandDetails> approvedLandList = landManagementService.getApprovedLandsByGNDivision(Integer.parseInt(id), user);
        List<Land> approveList = new ArrayList<Land>();
        for (Land land : approvedLandList) {
            if (landAssociationDao.getAllNotApprovedAlienationMethodMetadataByLand(land.getId(), user) != null) {
                approveList.add(land);
            }
        }
        return populateBasicLandDetails(approveList, user);

    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Land> getAllLandAssociationsToBeApprovedByUser(User user) {
        logger.debug("Loading All land associations to be approved  of GN Division {} by {}", user.getUsername());
        List<Land> approvedLandList = landDAO.getAllApprovedLandsByUser(user);

        // List<LandDetails> approvedLandList = landManagementService.getApprovedLandsByGNDivision(Integer.parseInt(id), user);
        List<Land> approveList = new ArrayList<Land>();
        for (Land land : approvedLandList) {
            if (landAssociationDao.getAllNotApprovedAlienationMethodMetadataByLand(land.getId(), user) != null) {
                approveList.add(land);
            }
        }
        return approveList;

    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getLandDefineAMAssociationsToBeApproved(int gnDivisionId, User user) {
        logger.debug("Loading land DefineAM associations to be approved  of GN Division {} by {}", gnDivisionId, user.getUsername());
        //List<Land> approvedLandList = landDAO.getApprovedLandsByGNDivision(gnDivisionId, user);

        List<Land> approvedLandList = new ArrayList<Land>();
        if (gnDivisionId == -1 && user.getDsDivisions() != null && user.getDsDivisions().size() > 0) {
            approvedLandList = landDAO.searchAllActiveApprovedLandsOfDSDivision(user.getDsDivisions().iterator().next().getId());
        } else {
            approvedLandList = landDAO.getApprovedLandsByGNDivision(gnDivisionId, user);
        }

        // List<LandDetails> approvedLandList = landManagementService.getApprovedLandsByGNDivision(Integer.parseInt(id), user);
        List<Land> approveList = new ArrayList<Land>();
        for (Land land : approvedLandList) {
            if (landAssociationDao.getAllNotApprovedDefineAlienationMethodMetadataByLand(land.getId(), user) != null) {
                approveList.add(land);
            }
        }
        return populateBasicLandDetails(approveList, user);

    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Land> getAllLandDefineAMAssociationsToBeApprovedByUser(User user) {
        logger.debug("Loading All land DefineAM associations to be approved   by {}", user.getUsername());
        List<Land> approvedLandList = landDAO.getAllApprovedLandsByUser(user);

        List<Land> approveList = new ArrayList<Land>();
        for (Land land : approvedLandList) {
            if (landAssociationDao.getAllNotApprovedDefineAlienationMethodMetadataByLand(land.getId(), user) != null) {
                approveList.add(land);
            }
        }
        return approveList;
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getLandToBeDefineAMAssociations(int gnDivisionId, User user) {
        logger.debug("Loading land ToBeDefineAM associations   of GN Division {} by {}", gnDivisionId, user.getUsername());

        List<Land> approveList = new ArrayList<Land>();

        List<lk.icta.core.entity.LandAssociation> metadatas = new ArrayList<LandAssociation>();

        if (gnDivisionId == -1) {
            metadatas = landAssociationDao.getAllNotApprovedDefineAlienationMetadataByUser(user);
        } else {
            metadatas = landAssociationDao.getAllNotApprovedDefineAlienationMetadataByGn(gnDivisionId, user);
        }

        if (metadatas != null && metadatas.size() > 0) {
            for (lk.icta.core.entity.LandAssociation metadata : metadatas) {
                approveList.add(metadata.getLand());
            }
        }
        return populateBasicLandDetails(approveList, user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandAssociationDetails> getLandToBeAssociations(int gnDivisionId, User user) {
        logger.debug("Loading land ToBe associations   of GN Division {} by {}", gnDivisionId, user.getUsername());


        //List<Land> approvedLandList = landDAO.getApprovedLandsByGNDivision(gnDivisionId, user);
        List<Land> approvedLandList = new ArrayList<Land>();
        //List<Land> approveList = new ArrayList<Land>();
        List<LandAssociationDetails> landAssociationDetailsList = new ArrayList<LandAssociationDetails>();

        if (gnDivisionId == -1 && user.getDsDivisions() != null && user.getDsDivisions().size() > 0) {
            //approvedLandList = landDAO.searchAllActiveApprovedLandsOfDSDivision(user.getDsDivisions().iterator().next().getId());
                approvedLandList = landDAO.searchAllActiveApprovedUnalienatedLandsOfDSDivision(user.getDsDivisions().iterator().next().getId());

        } else {
            //approvedLandList = landDAO.getApprovedLandsByGNDivision(gnDivisionId, user);
            approvedLandList = landDAO.getApprovedUnAlienatedLandsByGNDivision(gnDivisionId, user);
        }

        for (Land land : approvedLandList) {
            if (landAssociationDao.getAlienationMethodMetadataByLand(land.getId(), user) == null) {

                LandAssociationDetails details = new LandAssociationDetails();
                details.setId(land.getId());
                details.setNameOfTheLand(land.getNameOfTheLand());


                landAssociationDetailsList.add(details);
            }
        }
        //List<lk.icta.core.entity.LandAssociation> metadatas = landAssociationDao.getAllNotApprovedAssociationAlienationMethodMetadata(user);
        //List<lk.icta.core.entity.LandAssociation> metadatas = landAssociationDao.getAllNotApprovedAssociationAlienationMethodMetadataByUser(user);
        List<lk.icta.core.entity.LandAssociation> metadatas = new ArrayList<LandAssociation>();

        if (gnDivisionId == -1) {
            metadatas = landAssociationDao.getAllNotApprovedAssociationAlienationMethodMetadataByUser(user);
        } else {
            metadatas = landAssociationDao.getAllNotApprovedAssociationAlienationMethodMetadataByGn(gnDivisionId, user);
        }


        if (metadatas != null && metadatas.size() > 0) {
            for (lk.icta.core.entity.LandAssociation metadata : metadatas) {
                LandAssociationDetails details = new LandAssociationDetails();
                details.setId(metadata.getLand().getId());
                details.setNameOfTheLand(metadata.getLand().getNameOfTheLand());

                List<Integer> ids = new ArrayList<Integer>();
                for (AlienationMethod am : metadata.getAlienationMethods()) {
                    logger.debug("\n{}", am.getId());
                    ids.add(am.getId());
                    if (am.getId() == 1) {
                        details.setLandPermit(1);
                    } else if (am.getId() == 2) {
                        details.setLandGrant(1);
                    } else if (am.getId() == 3) {
                        details.setShortTerm(1);
                    } else if (am.getId() == 4) {
                        details.setLongTerm(1);
                    } else if (am.getId() == 5) {
                        details.setDep(1);
                    } else if (am.getId() == 6) {
                        details.setVesting(1);
                    } else if (am.getId() == 7) {
                        details.setSpecial(1);
                    }
                }
                details.setAlienationMethods(ids);
                landAssociationDetailsList.add(details);
            }
        }
        return landAssociationDetailsList;
    }

    //todo: to be refactored. select distinct lands from AMMetadata table
    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getLandAssociations(int gnDivisionId, User user) {
        logger.debug("Loading land associations of GN Division {} by {}", gnDivisionId, user.getUsername());
        List<Land> approvedLandList = landDAO.getApprovedLandsByGNDivision(gnDivisionId, user);
        List<Land> viewList = new ArrayList<Land>();
        for (Land land : approvedLandList) {
            if (landAssociationDao.getAlienationMethodMetadataListByLand(land.getId(), user) != null) {
                viewList.add(land);
            }
        }
        return populateBasicLandDetails(viewList, user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Land> searchActiveLandsOfDSDivisionByLandName(String landName, int dsId, User user) {
        logger.debug("Filter lands of DSDivision {} by land name {}", user.getUsername(), landName);
        return landDAO.searchActiveLandsOfDSDivisionByLandName(landName, dsId, user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Land> searchActiveLandsOfDSDivisionByLandName(String landName, User user) {
        logger.debug("Filter lands of DSDivision {} by land name {}", user.getUsername(), landName);
        return landDAO.searchActiveLandsOfDSDivisionByLandName(landName, user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Land> searchActiveLandsOfDSDivisionByPlanNo(String planNo, User user) {
        logger.debug("Filter lands of DSDivision {} by plan no {}", user.getUsername(), planNo);
        return landDAO.searchActiveLandsOfDSDivisionByPlanNo(planNo, user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Land> searchActiveLandsOfDSDivisionByLotNo(String lotNo, User user) {
        logger.debug("Filter lands of DSDivision {} by lot no {}", user.getUsername(), lotNo);
        return landDAO.searchActiveLandsOfDSDivisionByLotNo(lotNo, user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Land> searchActiveLandsOfDSDivisionByHolderName(String holderName, User user) {
        logger.debug("Filter lands of DSDivision {} by holder name {}", user.getUsername(), holderName);
        return landDAO.searchActiveLandsOfDSDivisionByHolderName(holderName, user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Land> searchAllActiveApprovedLandsOfDSDivision(int dsDivisionId) {
        logger.debug("Filter lands of DSDivision {}", dsDivisionId);
        return landDAO.searchAllActiveApprovedLandsOfDSDivision(dsDivisionId);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Land> searchAllActiveApprovedLandsOfDistrict(int districtId) {
        logger.debug("Filter lands of District {}", districtId);
        return landDAO.searchAllActiveApprovedLandsOfDistrict(districtId);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Land> searchChildrenByParentLand(User user, long landId) {
        logger.debug("search Children By Parent Land {}", user.getUsername());
        return landDAO.searchChildrenByParentLand(user, landId);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Land> searchAllChildrenByParentLand(User user, long landId) {
        logger.debug("search All Children By Parent Land {}", user.getUsername());
        return landDAO.searchAllChildrenByParentLand(user, landId);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Land> searchChildrenByParentReferenceNumber(User user, long landReferenceNumber) {
        logger.debug("searchChildrenByParentReferenceNumber {}", landReferenceNumber);
        return landDAO.searchChildrenByParentReferenceNumber(user, landReferenceNumber);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Land> searchLandByPlanNo(User user, String planNo) {
        logger.debug("searchLandByPlanNo {}", planNo);
        return landDAO.searchLandByPlanNo(user, planNo);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Land> searchLandByPlanNoAndLotNo(User user, String planNo, String lotNo) {
        logger.debug("searchLandByPlanNoAndLotNo {}", planNo, lotNo);
        return landDAO.searchLandByPlanNoAndLotNo(user, planNo, lotNo);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getAllActivePendingIncompleteLands(User user, int pageNo, int noOfRows) {
        logger.debug("Get ActivePendingIncomplete land records by {}", user.getUsername());
        return populateBasicLandDetails(landDAO.getAllActivePendingIncompleteLands(user, pageNo, noOfRows), user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getAllActivePendingIncompleteLandsByGNDivision(User user, int gnDivisionId,
                                                                            int pageNo, int noOfRows) {
        logger.debug("Get ActivePendingIncomplete land records by {}", user.getUsername());
        return populateBasicLandDetails(landDAO.getAllActivePendingIncompleteLandsByGNDivision(user, gnDivisionId, pageNo, noOfRows), user);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Land> searchAllLandsOfDSDivision(String landName,String planNo,String lotNo,String holderName,int dsDivisionId, int pageNo) {
        logger.debug("Filter All lands of DSDivision {}", dsDivisionId);
        return landDAO.searchAllLandsOfDSDivision(landName, planNo, lotNo, holderName,dsDivisionId, pageNo);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Land> searchAllLandsOfDistrict(String landName,String planNo,String lotNo,String holderName,int districtId, int pageNo) {
        logger.debug("Filter All lands of District {}", districtId);
        return landDAO.searchAllLandsOfDistrict(landName, planNo, lotNo, holderName, districtId, pageNo);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Land> searchAllLandsOfDSDivisionBySearchText(String landName,String planNo,String lotNo,String holderName, User user, int pageNo) {
        logger.debug("Filter All lands of DSDivision {} by land name: {}", user.getUsername(), landName);
        return landDAO.searchAllLandsOfDSDivisionBySearchText(landName, planNo, lotNo, holderName, user, pageNo);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Land> searchAllLandsOfManyDSDivisionsOfUpperLevel(String landName,String planNo,String lotNo,String holderName, User user, int pageNo) {
        logger.debug("Filter All lands of Many DSDivisions of User {} by page No: {}", user.getUsername(), pageNo);
        return landDAO.searchAllLandsOfManyDSDivisionsOfUpperLevel(landName,planNo,lotNo,holderName,user, pageNo);
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<Object[]> getLandStats(int id, String searchBy, Land.ApprovalState landState, Date fromDate,
                                       Date toDate, int migState) {
        return landDAO.getAllLandStatistics(id, searchBy, landState, fromDate, toDate, migState);

    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void updateLandApprovalNote(Land land, User user) {
        if (user.isAuthorized(Permission.DETAIL_LAND_DATA_MODIFICATION ) ||
                user.isAuthorized(Permission.ALIENATION_APPROVAL )) {
            landDAO.updateLand(land,user);
        }
    }

    @Transactional(propagation = Propagation.NEVER, readOnly = true)
    public List<LandDetails> getAllActivePendingIncompleteLandsByDsDivision(User user,int dsDivisionId, int pageNo, int noOfRows){
        logger.debug("Get ActivePendingIncomplete land records by dsDivision {}", dsDivisionId);
        return populateBasicLandDetails(landDAO.getAllActivePendingIncompleteLandsByDsDivision(user,dsDivisionId, pageNo, noOfRows), user);
    }

    private void handleELandRuntimeException(String message, int code) {
        logger.error(message);
        throw new ELandRuntimeException(message, code);
    }

    private void addLandAssociation(LandAssociation landAssociation, Land land, User user) {
        try {
            logger.debug("dd Land Association in Detail Land Application");
            if (landAssociation != null) {
                LandAssociation metadata = landAssociation;
                landAssociation = landAssociationDao.getAlienationMethodMetadataByLand(land.getId(), user);


                if (landAssociation != null) {

                    landAssociation.setLand(land);
                    landAssociation.setAlienationMethods(metadata.getAlienationMethods());
                    landAssociation.setPurpose(metadata.getPurpose());
                    landAssociation.setAssociationState(LandAssociation.AssociationState.DETAIL_LAND_DATA_ENTRY);
                    landAssociationDao.updateLandAlienationMetadata(landAssociation, user);

                } else {
                    if (metadata.getAlienationMethods() != null && metadata.getAlienationMethods().size() > 0) {
                        //Save New
                        landAssociation = new LandAssociation();
                        landAssociation.setLand(land);
                        landAssociation.setAlienationMethods(metadata.getAlienationMethods());
                        landAssociation.setPurpose(metadata.getPurpose());
                        landAssociation.setAssociationState(LandAssociation.AssociationState.DETAIL_LAND_DATA_ENTRY);
                        landAssociationDao.addLandAlienationMetadata(landAssociation, user);

                    }

                }

            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private List<LandDetails> populateBasicLandDetailsForInitReport(List<Land> landList, User user) {
        List<LandDetails> landDetailsList = new ArrayList<LandDetails>();
        for (Land land : landList) {
            LandDetails landDetail = new LandDetails();
            landDetail.setId(land.getId());
            landDetail.setNameOfTheLand(land.getNameOfTheLand());
            landDetail.setPlanNumber(land.getPlanNumber());
            landDetail.setLandExtent(land.getLandExtent());

            landDetail.setLotNumber(land.getLotNumber());

            landDetail.setVillage(land.getVillage());
            landDetailsList.add(landDetail);

            if (land.getAlienationState() != null && land.getAlienationState().equals(Land.AlienationState.UNALIENATED)) {

                if (AppConstants.ENGLISH.equals(user.getPrefLanguage())) {
                    landDetail.setAlienationMethod("Un-Alienated");
                } else if (AppConstants.SINHALA.equals(user.getPrefLanguage())) {
                    landDetail.setAlienationMethod("බැහැර නොකල ඉඩම්");
                } else if (AppConstants.TAMIL.equals(user.getPrefLanguage())) {
                    landDetail.setAlienationMethod("Un-Alienated");
                }
            } else {
                Alienation alienation = alienationManagementService.getActiveAlienationByLandId(land.getId(), user);


                if (AppConstants.ENGLISH.equals(user.getPrefLanguage())) {
                    landDetail.setAlienationMethod(alienation.getAlienationMethod().getEnName());
                } else if (AppConstants.SINHALA.equals(user.getPrefLanguage())) {
                    landDetail.setAlienationMethod(alienation.getAlienationMethod().getSiName());
                } else if (AppConstants.TAMIL.equals(user.getPrefLanguage())) {
                    landDetail.setAlienationMethod(alienation.getAlienationMethod().getTaName());
                }

                landDetail.setAlienationNumber(alienation.getId());
            }

            List<Schedule> scheduleList = scheduleManagementService.getScheduleByLandId(land.getId(), user);
            if (scheduleList != null && scheduleList.size() > 0) {
                Date scheduleDate = scheduleList.get(0).getEndDate();
                if (scheduleDate != null) {
                    landDetail.setScheduleDate(scheduleDate);
                }

            }

        }
        return landDetailsList;
    }

}


